#!/usr/bin/python3.6
import cgi
import cgitb
import json
import random
import re
import sqlite3
from enum import Enum
from typing import List

try:
    from person import anonymize_name, anonymize_text, Person, Gender
    from quote import highlight_brackets
    from quote import Quote
    from sanitize import sanitize_html
    from templating import render
    from utils import weighted_sample_without_replacement

    cgitb.enable()
    print("Content-type: text/html\n")

    form = cgi.FieldStorage()
    num_questions = form.getfirst("num-questions", 20)
    variants = form.getfirst("num-variants", 4)
    enabled_author_groups = form.getlist("author-group")

    connection = sqlite3.connect("quotes.sqlite")
    cursor = connection.cursor()

    author_counts_g = dict(
        (name, count)
        for name, count in cursor.execute(
            "SELECT author, COUNT(author) FROM quotes GROUP BY author"
        ).fetchall()
        if anonymize_name(name) != "[anonymní]"
    )

    with open("hlaskotest-data.json", encoding="utf8") as file:
        data = json.load(file)
    author_groups = data["author-groups"]

    answer_id = 0

    def generate_answer(text: str, correct=False) -> str:
        global answer_id
        id = answer_id
        answer_id += 1
        html = '<div class="answer-container"><input type="radio" class="answer"'
        if correct:
            html += " data-correct"
        html += f' id="answer-{id}" /><label for="answer-{id}">{text}</label></div>'
        return html

    def generate_question(text: str, correct: str, wrong: List[str]) -> str:
        answers = [generate_answer(correct, correct=True)] + [
            generate_answer(ans) for ans in wrong
        ]
        random.shuffle(answers)
        return '<div class="question">' f'{text}{"".join(answers)}</div>'

    def reveals_gender(text: str) -> bool:
        """Tries to guess whether the author's gender can be inferred from the quote text."""
        return bool(re.search(r"\b(jsem|bych)\b", text, re.IGNORECASE))

    def author_question(quote: Quote, group: List[str]) -> str:
        author_counts = dict(author_counts_g)
        author_counts[quote.author.actual_name] = 0
        correct_gender = quote.author.gender
        needs_correct_gender = reveals_gender(quote.text)
        for author_name in author_counts:
            if group and author_name not in group:
                author_counts[author_name] = 0
            elif needs_correct_gender:
                author = Person.lookup_or_create(author_name)
                if author.gender != correct_gender:
                    author_counts[author_name] = 0
        return generate_question(
            '<span class="question-text">Kdo je autorem této hlášky?</span>'
            f'<q class="text">{highlight_brackets(anonymize_text(sanitize_html(quote.text)))}</q>'
            f'<i class="context answered-only">{anonymize_text(sanitize_html(quote.context))}</i>',
            correct=sanitize_html(quote.author.name),
            wrong=[
                sanitize_html(anonymize_name(name))
                for name in weighted_sample_without_replacement(
                    list(author_counts.keys()),
                    list(author_counts.values()),
                    variants - 1,
                )
            ],
        )

    def quote_question(quote: Quote, group: List[str]) -> str:
        group = [name for name in group if name != quote.author.actual_name]
        if group:
            query = f"SELECT text FROM quotes WHERE author IN ({', '.join('?' for _ in group)}) ORDER BY RANDOM() LIMIT ?"
            args = [*group, variants - 1]
        else:
            query = (
                "SELECT text FROM quotes WHERE author <> ? ORDER BY RANDOM() LIMIT ?"
            )
            args = [quote.author.actual_name, variants - 1]
        wrong = [text for (text,) in cursor.execute(query, args).fetchall()]
        return generate_question(
            '<span class="question-text">'
            f'Kterou z těchto hlášek řekl{"a" if quote.author.gender == Gender.Female else ""} '
            f'<span class="author">{sanitize_html(quote.author.name)}</q></span>?</span>',
            correct='<span class="text">'
            + highlight_brackets(anonymize_text(sanitize_html(quote.text)))
            + "</span>",
            wrong=[
                '<span class="text">'
                + highlight_brackets(anonymize_text(sanitize_html(text)))
                + "</span>"
                for text in wrong
            ],
        )

    class QuestionType(Enum):
        AuthorOfQuote = 0
        QuoteOfAuthor = 1

    def question(group: List[str]) -> List[str]:
        question_type = random.choice(list(QuestionType))
        if group:
            query = f"""SELECT {','.join(Quote.columns)}
                        FROM quotes
                        WHERE author IN ({', '.join('?' for _ in group)})
                        ORDER BY RANDOM() LIMIT 1"""
            quote = Quote.from_row(cursor.execute(query, [*group]).fetchone())
        else:
            query = f"""SELECT {','.join(Quote.columns)}
                        FROM quotes
                        ORDER BY RANDOM() LIMIT 1"""
            quote = Quote.from_row(cursor.execute(query).fetchone())
        if question_type == QuestionType.AuthorOfQuote:
            return author_question(quote, group)
        elif question_type == QuestionType.QuoteOfAuthor:
            return quote_question(quote, group)

    questions = ""
    if enabled_author_groups:
        for _ in range(num_questions):
            group = random.choice(enabled_author_groups)
            questions += question(author_groups[group])
    else:
        for _ in range(num_questions):
            questions += question([])

    with open("templates/hlaskotest.html", encoding="utf-8") as file:
        template = file.read()
    print(
        render(
            template,
            {
                "questions": questions,
                **dict(
                    (
                        f"author_group_{group.replace('-', '_')}",
                        "checked" * int(group in enabled_author_groups),
                    )
                    for group in author_groups
                ),
            },
        )
        .encode("ascii", "xmlcharrefreplace")
        .decode("ascii")
    )
except Exception as e:
    print(e)
